package pkg

import (
	"fmt"
	"gctl/tpl/bff/cmd"
	"gctl/tpl/bff/cmd/apps"
	"gctl/tpl/bff/internals/controller/vi"
	"gctl/tpl/bff/internals/router"
	"gctl/tpl/curd"
	"gctl/tpl/pkg/storage/ali"
	"gctl/tpl/pkg/storage/cache/redis"
	"gctl/tpl/pkg/storage/chain/jaeger"
	"gctl/tpl/pkg/storage/conf"
	"gctl/tpl/pkg/storage/db/mysql"
	"gctl/tpl/pkg/storage/registry/consul"
	"gctl/tpl/pkg/storage/verify"
	cmd2 "gctl/tpl/srv/cmd"
	apps2 "gctl/tpl/srv/cmd/apps"
	"gctl/tpl/srv/internals/logics"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"text/template"
)

type TemplateData struct {
	App      string
	Resource string
}

type UnifiedConfig struct {
	Type     string // "dir" 或 "file"
	Path     string
	Template string // 仅文件需要
	Desc     string // 仅目录需要
}

func FileInit(app, name, file string) {
	base := func(p ...string) string { return filepath.Join(append([]string{app, name}, p...)...) }

	configs := []UnifiedConfig{
		{Type: "dir", Path: app, Desc: "项目"},
		{Type: "dir", Path: base(), Desc: "模块根目录"},
	}

	switch file {
	case "bff":
		configs = append(configs,
			dir(base("internal", "middleware"), "中间件"),
			files(base("main.go"), cmd.BffMainTpl),
			files(base("cmd", "root.go"), apps.RootTpl),
			files(base("cmd", "apps", "config.yaml"), apps.Yaml),
			files(base("internal", "controller", "vi", "demo.go"), vi.ViDemoTpl),
			files(base("internal", "controller", "vi", "getDemo.go"), vi.GetDemoTpl),
			files(base("internal", "router", "router.go"), router.RouterTpl),
		)
	case "srv":
		configs = append(configs,
			dir(base("internal", "model"), "模型层"),
			dir(base("internal", "model", "dao"), "dao"),
			dir(base("internal", "model", "dto"), "dto"),
			dir(base("internal", "model", "po"), "po"),
			files(base("main.go"), cmd2.MainTpl),
			files(base("cmd", "root.go"), apps2.RootTpl),
			files(base("cmd", "apps", "config.yaml"), apps2.Yaml),
			files(base("internal", "logic", "demoLogic.go"), logics.LogicTpl),
		)
	case "pkg":
		storage := func(p ...string) string { return base("storage", filepath.Join(p...)) }
		configs = append(configs,
			dir(base("logger"), "日志模块"),
			dir(base("util"), "工具模块"),
			files(storage("ali", "pay.go"), ali.PayTpl),
			files(storage("conf", "conf.go"), conf.ConfTpl),
			files(storage("conf", "nacos.go"), conf.NacosTpl),
			files(storage("chain", "jaeger.go"), jaeger.JaegerTpl),
			files(storage("consul", "consul.go"), consul.ConsulTpl),
			files(storage("db", "mysql.go"), mysql.MysqlTpl),
			files(storage("verify", "init.go"), verify.VerifyInitTpl),
			files(storage("verify", "demo.go"), verify.VerifyDemoTpl),
			files(storage("redis", "redis.go"), redis.RedisTpl),
		)
	default:
		ExitWithError(fmt.Errorf("未知模块类型: %s", file))
	}

	FileInits(app, configs)
}

func FileInits(app string, configs []UnifiedConfig) {
	templateData := TemplateData{App: app}
	for _, config := range configs {
		switch config.Type {
		case "dir":
			if err := CreateDir(config.Path, config.Desc); err != nil {
				ExitWithError(err)
			}
		case "file":
			if err := GenerateFile(config.Path, config.Template, templateData); err != nil {
				ExitWithError(fmt.Errorf("生成 %s 失败: %v", filepath.Base(config.Path), err))
			}
		}
	}
	err := os.Chdir(app)
	if err != nil {
		fmt.Printf("Error change directory: %v\n", err)
		os.Exit(1)
	}
	var wg sync.WaitGroup // 声明 WaitGroup
	wg.Add(1)

	go func() {
		defer wg.Done()
		// 检查或创建 go.mod
		if _, err = os.Stat("go.mod"); os.IsNotExist(err) {
			runCommand("go", "mod", "init", app)
		}

		// 确保在 go.mod 存在后执行
		//runCommand("go", "mod", "tidy")
	}()
	wg.Wait() // 等待协程执行完成
}

func CreateDir(path, dirType string) error {
	if err := os.MkdirAll(path, os.ModePerm); err != nil {
		return fmt.Errorf("create %s directory failed: %v", dirType, err)
	}
	return nil
}

func GenerateFile(filePath, templateContent string, data TemplateData) error {

	dirPath := filepath.Dir(filePath)
	if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
		return fmt.Errorf("目录创建失败 [%s]: %v", dirPath, err)
	}

	tmpl, err := template.New("template").Parse(templateContent)
	if err != nil {
		return err
	}

	file, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	return tmpl.Execute(file, data)
}

func ExitWithError(err error) {
	fmt.Printf("Error: %v\n", err)
	os.Exit(1)
}

// 执行shell命令
func runCommand(name string, args ...string) {
	fmt.Printf("Executing: %s %s\n", name, strings.Join(args, " "))

	cmd := exec.Command(name, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	if err != nil {
		fmt.Printf("Command failed: %v\n", err)
		os.Exit(1)
	}
}

// 辅助函数
func dir(path, desc string) UnifiedConfig {
	return UnifiedConfig{Type: "dir", Path: path, Desc: desc}
}

func files(path, template string) UnifiedConfig {
	return UnifiedConfig{Type: "file", Path: path, Template: template}
}

func CurdFile(app, name string) {
	base := func(p ...string) string { return filepath.Join(append([]string{app}, p...)...) }
	configs := []UnifiedConfig{}
	configs = append(configs,
		files(base("curdDemo", "curd.go"), curd.CurdTpl),
	)

	templateData := TemplateData{Resource: name}
	for _, config := range configs {
		if err := GenerateFile(config.Path, config.Template, templateData); err != nil {
			ExitWithError(fmt.Errorf("生成 %s 失败: %v", filepath.Base(config.Path), err))
		}
	}
}
